/*	Interactive Gravitational Simulator
	Project: IGS Core
	File: FrameCounter.h
	Author: Mike Bantegui <mbante2@gmail.com>, Hofstra University 
	Copyright (C) 2012 - 2013 Mike Bantegui

	This file is part of the IGS software package for simulating
	the N-Body problem in real-time. This was developed as part
	of a departmental honors thesis project at Hofstra University
	in the Spring 2012 semester.

	IGS is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	IGS is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef IGS_FRAMECOUNTER_H
#define IGS_FRAMECOUNTER_H

#include <algorithm>
#include <cassert>
#include <cmath>

namespace IGS
{

template <class Func>
struct Param;

template <class R>
struct Param<R()>
{
	typedef R ReturnType;
};

template <class TimerFunc>
class FrameCounter
{
public:
	typedef typename Param<TimerFunc>::ReturnType ReturnType;

private:
	// The underlying timer function
	TimerFunc &_Timer;
	// The number of samples to use
	const int _SampleCount;
	// The scale factor for timings
	const double _Scale;
	// The total number of samples taken
	int _TotalSamples;
	// Whether the timer is running
	bool _IsRunning;
	// The timestamp for the current frame
	ReturnType _CurrentFrameTime;
	// The timestamp for the last frame
	ReturnType _LastFrameTime;
	// The current frame time 
	ReturnType _FrameTime;
	// The average frame time
	double _AverageFrameTime;

public:
	// Construt an instance of the FrameCounter with the specified
	// timer function and sample count n
	FrameCounter(int n, TimerFunc timer, double scale) : _Timer(timer), 
		_SampleCount(std::max(5, n)), _Scale(scale)
	{
		Reset();
	}

	// The number of samples taken
	int SampleCount() const
	{
		return _SampleCount;
	}

	// The scale factor
	double Scale() const
	{
		return _Scale;
	}
	
	// Get the total number of samples taken
	int TotalSamples() const
	{
		return _TotalSamples;
	}

	// Whether the frame rate counter is running
	bool IsRunning() const
	{
		return _IsRunning;
	}

	// The average time between frames
	double AverageFrameTime() const
	{
		return _AverageFrameTime / _Scale;
	}

	// The current frame time
	double FrameTime() const
	{
		return _FrameTime / _Scale;
	}

	// The average frame rate
	double AverageFPS() const
	{
		if (_AverageFrameTime == 0) return 0;
		return 1 / AverageFrameTime();
	}

	// Reset the timer to it's initial state
	void Reset()
	{
		_IsRunning = false;
		_TotalSamples = 0;
		_CurrentFrameTime = _Timer();
		_LastFrameTime = _Timer();
		_AverageFrameTime = 0;
	}

	// Start the timer to take a frame time sample
	void Start()
	{
		assert(!_IsRunning);
		_IsRunning = true;

		_CurrentFrameTime = _Timer();
	}

	// Stop the timer to obtain the frame time sample
	void Stop()
	{
		assert(_IsRunning);
		_IsRunning = false;

		_FrameTime = _CurrentFrameTime - _LastFrameTime;
		_LastFrameTime = _CurrentFrameTime;

		// Alternate alpha metric. W controls the time period
		// over which the avraging occurs. 
		// const int targetFPS = 60;
		// const double W = (_SampleCount / targetFPS);
		// double a = 1 - exp(-1.0 * _FrameTime / W);
		double alpha = 2 / (1.0 + _SampleCount);

		_AverageFrameTime = alpha * _FrameTime + (1 - alpha) * _AverageFrameTime;
		_TotalSamples++;
	}
};

}

#endif